<template>
  <form class="page page-raname-batch">
    <h4 class="text-center">批量修改文件名</h4>
    <div class="tabs-bar">
      <div v-for="(v, i) in tabsList" :key="i" :class="['tab-item', { active: i === tabActiveIndex }]"
        @click="tabItemClick(i)">{{ v.label }}</div>
    </div>
    <div class="fieldset-options row">
      <div class="col-9">
        <fieldset :class="`fieldset-${tabActive.value}`">
          <legend>参数设置</legend>
          <template v-if="tabActive.value === 'delete'">
            <div class="form-group row-flex" align="center">
              <div class="row-flex-prefix">
                <input class="form-check-input" type="radio" value="deleteByStr" v-model="deleteForm.type">按字符串删除
              </div>
              <div class="row-flex-main">
                <input class="form-control form-control" type="text" v-model="deleteForm.str"
                  placeholder="请输入要删除的字符串" />
              </div>
            </div>
            <div class="form-group row-flex" align="center">
              <div class="row-flex-prefix">
                <input class="form-check-input" type="radio" value="deleteByOrder" v-model="deleteForm.type">按序号删除
              </div>
              <div class="row-flex-main row">
                <div class="col-4">
                  <div class="input-group">
                    <div class="input-group-prepend">
                      <span class="input-group-text">开始序号</span>
                    </div>
                    <input class="form-control form-control" type="number" v-model="deleteForm.startIndex" />
                  </div>
                </div>
                <div class="col-4">
                  <div class="input-group">
                    <div class="input-group-prepend">
                      <span class="input-group-text">删除个数</span>
                    </div>
                    <input class="form-control form-control" type="number" min="0" v-model="deleteForm.count" />
                  </div>
                </div>
                <div class="col-4">
                  <div class="input-group">
                    <div class="input-group-prepend">
                      <div class="input-group-text">
                        <input type="checkbox" class="mt-1 mb-2" v-model="deleteForm.reverse">
                      </div>
                    </div>
                    <input type="text" class="form-control" value="从末尾开始" readonly>
                  </div>
                </div>
              </div>
            </div>
          </template>
          <template v-else-if="tabActive.value === 'order'">
            <div class="form-group row-flex" align="center">
              <div class="row-flex-prefix">格式</div>
              <div class="row-flex-main">
                <select class="form-control" v-model="orderForm.format" placeholder="请选择文件名格式">
                  <option v-for="(v, i) in orderForm.formatList" :value="v" :key="i">{{ v }}</option>
                </select>
              </div>
              <div class="row-flex-suffix text-danger">注：“#”表示序号，&lt;SELF&gt;表示原文件名</div>
            </div>
            <div class="form-group row-flex" align="center">
              <div class="row-flex-prefix">序号</div>
              <div class="row-flex-main row">
                <div class="col-6">
                  <div class="input-group">
                    <div class="input-group-prepend">
                      <span class="input-group-text">起始序号</span>
                    </div>
                    <input class="form-control form-control" type="number" v-model="orderForm.startIndex" />
                  </div>
                </div>
                <div class="col-6">
                  <div class="input-group">
                    <div class="input-group-prepend">
                      <span class="input-group-text">增量</span>
                    </div>
                    <input class="form-control form-control" type="number" min="1" v-model="orderForm.increment" />
                  </div>
                </div>
              </div>
            </div>
            <div class="form-group row-flex" align="center">
              <div class="row-flex-prefix">位数</div>
              <div class="row-flex-main row">
                <div class="col-6">
                  <div class="input-group">
                    <div class="input-group-prepend">
                      <span class="input-group-text">位数</span>
                    </div>
                    <input class="form-control form-control" type="number" min="1" v-model="orderForm.size" />
                  </div>
                </div>
                <div class="col-6">
                  <div class="input-group">
                    <div class="input-group-prepend">
                      <div class="input-group-text">
                        <input type="checkbox" class="mt-1 mb-2" v-model="orderForm.autoComplete">
                      </div>
                    </div>
                    <input type="text" class="form-control" value="根据文件数自动补齐" readonly>
                  </div>
                </div>
              </div>
            </div>
          </template>
          <template v-else-if="tabActive.value === 'add'">
            <div class="form-group row">
              <div class="col-6">
                <div class="input-group">
                  <div class="input-group-prepend">
                    <span class="input-group-text">文件名前添加</span>
                  </div>
                  <input class="form-control form-control" type="text" v-model="addForm.prefix" placeholder="请输入字符串" />
                </div>
              </div>
              <div class="col-6">
                <div class="input-group">
                  <div class="input-group-prepend">
                    <span class="input-group-text">文件名后添加</span>
                  </div>
                  <input class="form-control form-control" type="text" v-model="addForm.suffix" placeholder="请输入字符串" />
                </div>
              </div>
            </div>
            <div class="form-group">
              <div class="input-group">
                <div class="input-group-prepend">
                  <span class="input-group-text">在文件名中第</span>
                </div>
                <input class="form-control" style="flex-grow:.1" type="number" min="1" v-model="addForm.index" />
                <div class="input-group-prepend">
                  <span class="input-group-text">个字符后添加</span>
                </div>
                <input class="form-control" type="text" v-model="addForm.indexStr" placeholder="请输入字符串" />
              </div>
            </div>
          </template>
          <template v-else-if="tabActive.value === 'replace'">
            <div class="form-group row-flex" align="center">
              <div class="row-flex-prefix">被替换</div>
              <div class="row-flex-main">
                <input class="form-control" type="text" v-model="replaceForm.oldStr" placeholder="请输入被替换的旧字符串" />
              </div>
            </div>
            <div class="form-group row-flex" align="center">
              <div class="row-flex-prefix">替换为</div>
              <div class="row-flex-main">
                <input class="form-control" type="text" v-model="replaceForm.newStr" placeholder="请输入要替换的新字符串" />
              </div>
            </div>
            <div class="form-group row-flex" align="center">
              <div class="row-flex-prefix"></div>
              <div class="row-flex-main text-left">
                <input class="form-check-input" type="checkbox" v-model="replaceForm.useReg">
                <label class="form-check-label">使用正则表达式</label>
              </div>
            </div>
          </template>
          <template v-else-if="tabActive.value === 'extension'">
            <div class="form-group row-flex" align="center">
              <div class="row-flex-prefix">更改为</div>
              <div class="row-flex-main">
                <input class="form-control" type="text" v-model="extensionForm.newExt" placeholder="请输入新扩展名" />
              </div>
              <div class="row-flex-suffix text-danger">注：不需要加点</div>
            </div>
            <div class="form-group row-flex" align="center">
              <div class="row-flex-prefix"></div>
              <div class="row-flex-main text-left">
                <div class="form-check form-check-inline" v-for="(v, i) in extensionForm.selectList" :key="i">
                  <input class="form-check-input" type="radio" :value="v.value" v-model="extensionForm.select">
                  <label class="form-check-label">{{ v.label }}</label>
                </div>
              </div>
            </div>
          </template>
          <template v-else-if="tabActive.value === 'datetime'">
            <div class="form-group row-flex" align="center">
              <div class="row-flex-prefix">文件名格式</div>
              <div class="row-flex-main">
                <select class="form-control" v-model="datetimeForm.format" placeholder="请选择文件名格式">
                  <option v-for="(v, i) in datetimeForm.formatList" :value="v" :key="i">{{ v }}</option>
                </select>
              </div>
            </div>
            <div class="form-group row-flex" align="center">
              <div class="row-flex-prefix"></div>
              <div class="row-flex-main text-left">
                <input class="form-check-input" type="checkbox" v-model="datetimeForm.useExif">
                <label class="form-check-label">读取照片的Exif信息</label>
              </div>
            </div>
            <div class="form-group row-flex" align="center" v-if="datetimeForm.useExif">
              <div class="row-flex-prefix">若无Exif信息</div>
              <div class="row-flex-main">
                <select class="form-control" v-model="datetimeForm.bakExif" placeholder="请选择">
                  <option v-for="v in datetimeForm.bakExifList" :value="v.value" :key="v.value">{{ v.label }}</option>
                </select>
              </div>
            </div>
            <div class="form-group row-flex" align="center" v-else>
              <div class="row-flex-prefix">日期时间</div>
              <div class="row-flex-main">
                <select class="form-control" v-model="datetimeForm.date" placeholder="请选择">
                  <option v-for="v in datetimeForm.dateList" :value="v.value" :key="v.value">{{ v.label }}</option>
                </select>
              </div>
            </div>
          </template>
        </fieldset>
      </div>
      <div class="col-3">
        <fieldset class="flex-center">
          <legend>操作</legend>
          <div class="form-group">
            <x-button type="default" block @click="preview" :disabled="loadingCount > 0">预览</x-button>
          </div>
          <div class="form-group">
            <x-button type="primary" block @click="execute" :loading="loadingCount > 0">执行修改</x-button>
          </div>
        </fieldset>
      </div>
    </div>
    <fieldset class="fieldset-content">
      <legend>文件列表</legend>
      <div class="row-flex" align="center">
        <div class="row-flex-main text-left">共 {{ filesList.length }} 个文件</div>
        <div class="row-flex-suffix">
          <button type="button" class="btn btn-link" @click="clearFilesList" title="清空">
            <i class="fa fa-trash-o"></i>
          </button>
        </div>
      </div>
      <div class="table-header" :style="thStyles">
        <table>
          <tbody>
            <tr>
              <th v-for="(v, i) in tableHeaders" :key="`th_${i}`" :style="{ width: v.width }">{{ v.label }}</th>
            </tr>
          </tbody>
        </table>
      </div>
      <div class="table-body" @drop.prevent="filesDrop" @dragover.prevent>
        <table ref="table">
          <tbody>
            <tr v-for="(v, i) in filesList" :key="`file_${i}`">
              <template v-for="(item, index) in tableHeaders">
                <td :style="{ width: item.width }" :key="`td_${i}_${index}`">
                  <button v-if="item.value === 'action'" type="button" class="btn btn-link" @click="deleteFileItem(i)"
                    title="删除">
                    <i class="fa fa-close"></i>
                  </button>
                  <span v-else-if="item.value === 'oldName'">{{ v[item.value] + v.ext }}</span>
                  <span v-else-if="item.value === 'newName'">{{ v[item.value]?(v[item.value] + v.newExt) : ''}}</span>
                  <span v-else>{{ v[item.value] || '' }}</span>
                </td>
              </template>
            </tr>
            <tr v-if="filesList.length === 0" class="nodata-tr">
              <td>将待处理的文件拖动到此处</td>
            </tr>
          </tbody>
        </table>
      </div>
    </fieldset>
  </form>
</template>

<script>
const fs = global.require('fs')
const path = global.require('path')
const ExifImage = global.ExifImage

export default {
  name: 'raname-batch',
  data() {
    return {
      filesList: [],
      tasksList: [],
      exifDict: {},
      deleteForm: {
        type: 'deleteByStr', // deleteByStr | deleteByOrder
        str: '',
        startIndex: 0,
        count: 0,
        reverse: false
      },
      orderForm: {
        format: '#',
        startIndex: 1,
        increment: 1,
        size: 2,
        autoComplete: false,
        formatList: [
          '#.<SELF>', '#', '#_<SELF>', '#<SELF>', '<SELF>#', '<SELF>_#',
          '<SELF>[#]', 'Pic_#', 'File_#', '图片#', '文件_#'
        ]
      },
      addForm: {
        prefix: '',
        suffix: '',
        index: 1,
        indexStr: '',
      },
      replaceForm: {
        oldStr: '',
        newStr: '',
        useReg: false,
      },
      extensionForm: {
        newExt: '',
        select: '',
        selectList: [
          { label: '全部大写', value: 'upperCase' },
          { label: '全部小写', value: 'lowerCase' },
          { label: '删除扩展名', value: 'delete' },
        ]
      },
      datetimeForm: {
        format: 'yyyyMMddhhmmss',
        date: 'now',
        useExif: false,
        bakExif: 'createTime',
        formatList: [
          'yyyyMMddhhmmss', 'yyyy-MM-dd hhmmss', 'yyyyMMdd',
          'yyyy年MM月dd日hh时mm分ss秒', 'yyyy年M月d日h时m分s秒', 'yy年M月d日h时m分s秒',
          '相片yyyy年MM月dd日', '<SELF>-yyyyMMddhhmmss',
        ],
        bakExifList: [
          { label: '使用文件创建日期', value: 'createTime' },
          { label: '使用文件修改日期', value: 'updateTime' },
          { label: '忽略不处理', value: 'ignore' },
        ],
        dateList: [
          { label: '使用文件创建日期', value: 'createTime' },
          { label: '使用文件修改日期', value: 'updateTime' },
          { label: '当前日期', value: 'now' },
        ]
      },
      tabActiveIndex: 0,
      tabsList: [
        { label: '删除', value: 'delete' },
        { label: '序号', value: 'order' },
        { label: '添加', value: 'add' },
        { label: '替换', value: 'replace' },
        { label: '扩展名', value: 'extension' },
        { label: '日期时间', value: 'datetime' },
      ],
      tableHeaders: [
        { label: '原文件名', value: 'oldName', width: '30%' },
        { label: '新文件名', value: 'newName', width: '30%' },
        { label: '扩展名', value: 'ext', width: '10%' },
        { label: '状态', value: 'status', width: '20%' },
        { label: '操作', value: 'action', width: '10%' },
      ],
      thStyles: {},
      loadingCount: 0
    }
  },
  computed: {
    tabActive() {
      return this.tabsList[this.tabActiveIndex] || {}
    },
    filePathsList() {
      return this.filesList.map(v => v.path)
    }
  },
  methods: {
    tabItemClick(index) {
      this.tabActiveIndex = index
    },
    // 拖拽待处理文件
    filesDrop(e) {
      const filesList = e.dataTransfer.files
      if (!filesList) return
      for (let i = 0; i < filesList.length; i++) {
        const file = filesList[i]
        if (!this.filePathsList.includes(file.path)) {
          let fileStat = fs.statSync(file.path)
          let oldName = path.basename(file.path) || file.name
          if (/.(gif|jpg|jpeg|png|gif|jpg|png)$/i.test(oldName)) {
            // 获取exif信息
            this.getExifInfo(file.path)
          }
          let oldNameArr = oldName.split('.')
          if (oldNameArr.length > 1) {
            oldNameArr = oldNameArr.splice(0, oldNameArr.length - 1)
          }
          oldName = oldNameArr.join('.')
          this.filesList.push({
            path: file.path,
            oldName,
            newName: '',
            dirName: path.dirname(file.path),
            ext: path.extname(file.path),
            newExt: path.extname(file.path),
            status: '',
            updateTime: fileStat.mtime,
            createTime: fileStat.birthtime,
          })
        }
      }
      this.checkScroll()
    },
    getExifInfo(filePath) {
      let _this = this
      if (!ExifImage) return
      new ExifImage(
        { image: filePath },
        function (error, exifData) {
          if (error) { console.log('Error: ', error.message) }
          else {
            let exifInfo = { ...(exifData.exif || {}) }
            if (exifInfo.CreateDate) {
              let arr = exifInfo.CreateDate.split(' ')
              arr[0] = arr[0].replace(/:/g, '-')
              exifInfo.CreateDate = arr.join(' ')
            }
            _this.exifDict[filePath] = exifInfo
          }
        }
      )
    },
    // 删除条目
    deleteFileItem(index) {
      this.filesList.splice(index, 1)
      this.checkScroll()
    },
    checkScroll() {
      this.$nextTick(() => {
        if (!this.$refs.table) return
        let tableHeight = this.$refs.table.clientHeight
        let tableParentHeight = this.$refs.table.parentNode.clientHeight
        if (tableHeight > tableParentHeight) {
          this.thStyles = { overflow: 'hidden scroll' }
        } else {
          this.thStyles = { overflow: 'hidden' }
        }
      })
    },
    // 清空待处理文件
    clearFilesList() {
      this.filesList = []
      this.tasksList = []
      this.thStyles = {}
      this.loadingCount = 0
    },
    // 预览
    preview() {
      let dateCount = {}
      let dateCount2 = {}
      let filesList = this.$jstools.deepCopy(this.filesList)
      if (this.tabActive.value === 'datetime') {
        filesList.forEach(v => {
          let date = Date.now()
          if (this.datetimeForm.useExif) {
            date = (this.exifDict[v.path] || {}).CreateDate || v[this.datetimeForm.bakExif] || ''
          } else if (this.datetimeForm.date === 'createTime') {
            date = v.createTime
          } else if (this.datetimeForm.date === 'updateTime') {
            date = v.updateTime
          }
          if (date) {
            date = this.$jstools.formatDate(date, 'yyyyMMddhhmmss')
            dateCount[date] = (dateCount[date] || 0) + 1
          }
        })
      }
      this.filesList = filesList.map((v, i) => {
        if (this.tabActive.value === 'delete') {
          if (this.deleteForm.type === 'deleteByStr') {
            v.newName = v.oldName.replace(new RegExp(this.deleteForm.str, 'g'), '')
          } else {
            let oldNameArr = v.oldName.split('')
            let startIndex = this.deleteForm.reverse ? -this.deleteForm.startIndex : this.deleteForm.startIndex
            oldNameArr.splice(startIndex, this.deleteForm.count)
            v.newName = oldNameArr.join('')
          }
        } else if (this.tabActive.value === 'order') {
          if (this.orderForm.autoComplete) {
            let endIndex = this.orderForm.startIndex + (this.filesList.length - 1) * this.orderForm.increment
            this.orderForm.size = endIndex.toString().length
          }
          let index = (this.orderForm.startIndex + i * this.orderForm.increment).toString().padStart(this.orderForm.size, 0)
          let newName = this.orderForm.format.replace('#', index).replace('<SELF>', v.oldName)
          v.newName = newName
        } else if (this.tabActive.value === 'add') {
          let oldNameArr = v.oldName.split('')
          oldNameArr.splice(this.addForm.index, 0, this.addForm.indexStr)
          v.newName = this.addForm.prefix + oldNameArr.join('') + this.addForm.suffix
        } else if (this.tabActive.value === 'replace') {
          let reg = this.replaceForm.useReg ? new RegExp(this.replaceForm.oldStr) : new RegExp(this.replaceForm.oldStr, 'g')
          v.newName = v.oldName.replace(reg, this.replaceForm.newStr)
        } else if (this.tabActive.value === 'extension') {
          let ext = v.ext
          if (this.extensionForm.newExt) {
            ext = `.${this.extensionForm.newExt}`
          }
          if (this.extensionForm.select === 'delete') {
            v.newExt = ''
          } else if (this.extensionForm.select === 'upperCase') {
            v.newExt = ext.toUpperCase()
          } else if (this.extensionForm.select === 'lowerCase') {
            v.newExt = ext.toLowerCase()
          } else {
            v.newExt = ext
          }
          v.newName = v.oldName
        } else if (this.tabActive.value === 'datetime') {
          let date = Date.now()
          if (this.datetimeForm.useExif) {
            date = (this.exifDict[v.path] || {}).CreateDate || v[this.datetimeForm.bakExif] || ''
          } else if (this.datetimeForm.date === 'createTime') {
            date = v.createTime
          } else if (this.datetimeForm.date === 'updateTime') {
            date = v.updateTime
          }
          if (date) {
            let _date = this.$jstools.formatDate(date, 'yyyyMMddhhmmss')
            let newName = this.$jstools.formatDate(date, this.datetimeForm.format)
            if (dateCount[_date] > 1) {
              dateCount2[_date] = (dateCount2[_date] || 0) + 1
              newName += `(${dateCount2[_date]})`
            }
            v.newName = newName
          } else {
            v.newName = v.oldName
          }
        }
        return v
      })
    },
    // 执行修改
    execute() {
      this.preview()
      this.filesList.forEach((v, i) => {
        let newPath = path.join(v.dirName, v.newName + v.newExt)
        if (v.oldName === v.newName && v.ext === v.newExt) return
        this.loadingCount++
        this.tasksList.push(
          this.rename(v.path, newPath).then(() => {
            this.$set(this.filesList[i], 'status', '修改成功')
            this.$set(this.filesList[i], 'oldName', v.newName)
            this.$set(this.filesList[i], 'newName', '')
            this.$set(this.filesList[i], 'path', newPath)
            this.$set(this.filesList[i], 'ext', v.newExt)
          }).catch((err) => {
            console.log(err)
            this.$set(this.filesList[i], 'status', '修改失败')
          }).finally(() => {
            this.loadingCount--
          })
        )
      })
      Promise.all(this.tasksList).finally(() => {
        this.tasksList = []
      })
    },
    rename(from, to) {
      return new Promise((resolve, reject) => {
        fs.rename(from, to, (err) => {
          if (err) { reject(err) }
          else { resolve() }
        })
      })
    }
  }
}
</script>

<style scoped lang="less">
@import "~@/assets/css/_mixins.less";
@import "~@/assets/css/_color.less";

.page-raname-batch {
  display: flex;
  flex-direction: column;

  .row-flex-prefix {
    text-align: left;
  }

  fieldset {
    margin-top: 1em;
  }

  .fieldset-delete,
  .fieldset-datetime {
    .row-flex-prefix {
      width: 8em;
    }
  }

  .fieldset-order {
    .row-flex-prefix {
      width: 3em;
    }
  }

  .fieldset-replace,
  .fieldset-extension {
    .row-flex-prefix {
      width: 4em;
    }
  }

  .fieldset-content {
    height: 0;
    flex-grow: 1;

    .table-header,
    .table-body {
      table {
        width: 100%;
        border: 1px solid @borderGrey;

        tr {
          height: 2em;
        }

        th,
        td {
          border: 1px solid @borderGrey;
          text-align: center;
          vertical-align: middle;
        }

        .nodata-tr {
          height: 100%;
          font-size: 16px;
          color: #999;
        }
      }
    }

    .table-body {
      height: calc(100% - 6.5em);
      overflow: auto;

      table {
        min-height: 100%;
      }
    }
  }

  .fieldset-options {
    fieldset {
      height: calc(100% - 1em);
    }
  }
}
</style>
